#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include "Thread/ThreadPool.h"
#include "Util/onceToken.h"
#include "UpgraderServer.h"
#include "ConfigApi.h"
#include "Network/Socket.h"
#include "Protocol/ProtocolApi.h"

UpgraderServer *UpgraderServer::s_UpgraderServer = nullptr;

using namespace Protocol;
using namespace Fdt;
using namespace std;
const string cloudUpgradePath =  "/var/upfile";

#define PING_CMD	"/bin/ping -c 1 -W 2 %s > %s"
#define TMP_PING_PATH	"/var/pingInternet"
#define RECV1ICMPPKT	"1 packets received"
#define PING_URL	"8.8.8.8"

extern "C" int upgrade_complete(int hid, int crc_success);
extern "C" int upgrade_write(const void *buffer, size_t size);
extern "C" int upgrade_verify(int hid, const char *md5);
int MDNS_unRegisterService(void)
{
    return 0;
}


void UpgraderServerInit()
{
	UpgraderServer::instance();
}

UpgraderServer::UpgraderServer(){
	RegistRPC();
}


void UpgraderServer::RegistRPC(){
	RPCMethodMap method_map;
	method_map.emplace("Setup", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return Setup(request, inMap, response, outMap);
	}));

	method_map.emplace("Upgrade", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return Upgrade(request, inMap, response, outMap);
	}));

	method_map.emplace("Progress", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return Progress(request, inMap, response, outMap);
	}));

	method_map.emplace("Version", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return Version(request, inMap, response, outMap);
	}));

	method_map.emplace("CloudUpgrade", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return CloudUpgrade(request, inMap, response, outMap);
	}, true));

	method_map.emplace("CloudProgress", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return CloudProgress(request, inMap, response, outMap);
	}));

	method_map.emplace("Progress", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return Progress(request, inMap, response, outMap);
	}));
	
	IProtocolInterface::GetInstance()->RegistProtocol("Upgrade", method_map);
}


bool UpgraderServer::Setup(const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
	if(!verifySetup(request)){
		IProtocolInterface::GetInstance()->HttpSetError(response, API::InvalidArgs);
		return false;
	}

	return true;
}

bool UpgraderServer::Upgrade(const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
	API::ApiErr err = API::Success;
	std::string err_str;

    onceToken token(nullptr, [&response, &err, &err_str]() {
    	//tracef("OnceToken!!!!!!!!!!!!!!!!!!!!");
    	IProtocolInterface::GetInstance()->HttpSetError(response, err, err_str);
    });

	if(!verifyUpgrade(request, err_str)){
		err =  API::InvalidArgs;
		tracef("verifyParam error %s", request.toStyledString().c_str());
		return false;
	}

	err_str = "Upgrade ";
	int pos;
	Json::getJsonInt(request, "Param.Pos", pos);
	if(pos != _pos){
		 err = API::InvalidArgs;
		return false;
	}
	int slice;
	Json::getJsonInt(request, "Param.Size", slice);
	if(pos != _pos){
		 err = API::InvalidArgs;
		return false;
	}
	//tracef("pos------------>%d", pos);

	BufferPtr::Ptr bufferPtr =  inMap->get(_name);
	if(!bufferPtr){
		errorf("bufferPtr error");
		err_str = "Buffer error";
		err = API::InvalidArgs;
		return false;
	}
	if(slice != bufferPtr->size()){
		tracef("json size %d, buffer size %d", slice, bufferPtr->size());
		err_str = "Buffer error";
		err = API::InvalidArgs;
		return false;
	}

	//tracef("bufferPtr->size %d", bufferPtr->size());
	if(upgrade_write(bufferPtr->data(), bufferPtr->size())){
		errorf("file write error");
		err = API::OptFailed;
		return false;
	}

	_pos += bufferPtr->size();
	tracef("total %d, bytes %d", _total, _pos);
	if(_total == _pos){
		tracef("strHex2Val!!!!!!!!!!!");
		std::string md5 = strHex2Val(_md5.c_str(), _md5.size());
		if(upgrade_verify(0, md5.c_str())){
			err = API::OptFailed;
			return false;
		}
		//这个函数调用只有在VM33通过HTTP升级的时候使用，当使用NEARCAST升级的时候默认使用空实现
		MDNS_unRegisterService();
		ThreadPool::instance()->async([this](){
			upgrade_complete(0, 1);});

		return true;
	}

	return true;
}

bool UpgraderServer::Progress(const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){

	int percent;
	if (_total > 0)
	{
		percent = (_pos * 100)/_total;
		if (percent > 100)
		{
			percent = 100;
		}
		response["data"]["Param"]["Progress"]  = percent;
		response["data"]["Param"]["Result"]    = true;
	}
	else
	{
		response["data"]["Param"]["Result"]    = false;
	}
	return true;
}

bool UpgraderServer::Version(const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
	
	FILE * fp;
	char *line = NULL;
	size_t len = 0;
	ssize_t read;
	fp = fopen("/usr/cfg/version_cfg", "r");
	if (fp == NULL)
	{
		response["data"]["Param"]["Result"] = false;
		return false;
	}
	
	read = getline(&line, &len, fp);
	if (read <= 0)
	{
		if (line)
		{
			free(line);
		}
		fclose(fp);
		response["data"]["Param"]["Result"] = false;
		return false;
	}
	
	response["data"]["Param"]["Version"] = line;
	response["data"]["Param"]["Result"]   = true;

	if (line)
	{
		free(line);
	}
	fclose(fp);
	return true;
}

bool UpgraderServer::verifySetup(const Json::Value &request){
	if(!request.isMember("Param") || request["Param"].empty() ){
		return false;
	}

	const Json::Value &param = request["Param"];

	std::string type ;
	if(!Json::getJsonString(param, "Type", type)){
		errorf("type error");
		return false;
	}
	if(type != "Main"){
		errorf("type error");
		return false;
	}

	_upType = type;

	std::string fileName;
	if(!Json::getJsonString(param, "FileName", fileName)){
		errorf("FileName error");
		return false;
	}
	if(fileName.empty()){
		errorf("FileName error");
		return false;
	}
	_fileName = fileName;

	int total;
	if(!Json::getJsonInt(param, "Total", total)){
		errorf("Total error");
		return false;
	}
	if(total <= 0){
		errorf("Total error");
		return false;
	}
	_pos   = 0;
	_total = total;

	std::string md5;
	if(!Json::getJsonString(param, "Md5", md5)){
		errorf("Md5 error");
		return false;
	}
	if(md5.empty()){
		errorf("Md5 error");
		return false;
	}
	_md5 = md5;

	return true;
}


bool UpgraderServer::verifyUpgrade(const Json::Value &request, std::string &str_err){
	if(!request.isMember("Param") || request["Param"].empty() ){
		return false;
	}

	const Json::Value &param = request["Param"];
	str_err = "Verify ";
	std::string name ;
	if(!Json::getJsonString(param, "name", name)){
		str_err += "name ";
		tracef("name error");
		return false;
	}
	if(name.empty()){
		str_err += "name ";
		tracef("name error");
		return false;
	}
	_name = name;

	int pos;
	if(!Json::getJsonInt(param, "Pos", pos)){
		str_err += "Pos ";
		tracef("Pos error");
		return false;
	}
	if(pos < 0 || pos >= _total){
		str_err += "Pos ";
		tracef("Pos error");
		return false;
	}
	_pos = pos;

	int slice;
	if(!Json::getJsonInt(param, "Size", slice)){
		str_err += "Size ";
		tracef("Size error");
		return false;
	}
	if(slice <= 0 || pos+slice > _total){
		str_err += "Size ";
		tracef("Size error");
		return false;
	}

	return true;
}

bool UpgraderServer::checkInternetPing(void)
{
    char pingCMD[128]={0};
    char tmpBuf[512]={0};
    FILE *pfile = NULL;

    snprintf(pingCMD, sizeof(pingCMD), PING_CMD, PING_URL, TMP_PING_PATH);  /*ping GLOBAL DNS*/
    printf("pingCMD=%s!\n", pingCMD);
    system(pingCMD);
    if((pfile = fopen(TMP_PING_PATH, "rb")) != NULL) {
        fread(tmpBuf, sizeof(tmpBuf), 1, pfile);
        fclose(pfile);
    }
    else {
        printf("open ping file error!\n");
    }

    if(strstr(tmpBuf, RECV1ICMPPKT)) {  /*ping success*/
        return true;
    } else {                           /*ping fail*/
        return false;
    }

}

bool UpgraderServer::CloudUpgrade(const std::string& url){
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	_progress = 0;

	bool done = false;
	auto resultCb = [&done](const SockException &ex){
		if (ex) {
			WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
		} else {
			WarnL << "succeed"  << ex.what();
		}
		done = true;
	};

	IProtocolInterface::GetInstance()->HttpDownloadFile(url, 
		cloudUpgradePath, resultCb, _progress);
	while(_progress <= 100 && !done){
		usleep(1000);	
	}

	if(_progress == 100){
		upgrade_complete(0, 1);
		return true;
	}else{
		return false;
	}
}

bool UpgraderServer::CloudUpgrade(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outBuffer){
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	//参数校验
	if(!request.isMember("Param") || !request["Param"].isMember("Url") || !request["Param"]["Url"].isString()){
		return false;
	}
	//验证当前设备的外网通不通
	if(!checkInternetPing()){
		return false;
	}

	auto url = request["Param"]["Url"].asString();
	thread upgrade([this, url]{
		CloudUpgrade(url);
	});
	upgrade.detach();
	return true;
	// _progress = 0;
	
	// //升级
	// auto lam = [this, request](){
	// 	//下载升级包到指定的位置
	// 	bool done = false;
	// 	auto resultCb = [&done](const SockException &ex){
	// 		  if (ex) {
    //               //网络相关的错误
    //           	WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
    //           } else {
	// 			WarnL << "succeed"  << ex.what();
	// 		  }
	// 		  done = true;
	// 	};

	// 	IProtocolInterface::GetInstance()->HttpDownloadFile(request["Param"]["Url"].asString(), 
	// 		cloudUpgradePath, resultCb, _progress);
	// 	while(_progress <= 100 && !done){
	// 		usleep(1000);	
	// 	}
	// 	if(_progress == 100) upgrade_complete(0, 1);
		
	// };
	// thread upgrade(lam);
	// upgrade.detach();
	// return true;
}

bool UpgraderServer::CloudProgress(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outBuffer){
	response["data"]["Param"]["schedule"] = _progress;
	return true;
}

int UpgraderServer::CloudProgress(){
	return _progress;
}